Code Minimal Réseau - (4) Mes Wemos D1 Mini discutent sur Internet avec MQTT

Auteur avatarPhilippe Blusseau | Dernière modification 1/02/2023 par Philby

Code minimal des fonctions r seau WiKi Reseau1.png
Utilisation des fonction réseau des cartes compatibles Arduino, possédant une puce Wi-Fi
=== Episode n° 4 : Mes Wemos D1 Mini discutent sur Internet avec MQTT ===
Difficulté
Technique
Durée
2 heure(s)
Disciplines scientifiques
Arduino, Informatique
<languages />
Licence : Attribution (CC-BY)

Introduction

Cette expérience fait partie d'une série de 4 épisodes, présentant différentes façons de bénéficier des capacités de communication des cartes compatibles Arduino possédant une puce Wi-Fi (Wemos D1 mini, ESP32, ...). On suppose (Cf. "Expériences ré-requises" ci-après) que vous avez déjà manipulé une carte Arduino et son environnement de développement. Ces 4 épisodes sont les suivants :


  1. Connecter le Wemos D1 Mini à internet en Wi-Fi.
  2. Héberger un site web sur mon Wemos D1 Mini.
  3. Mon Wemos D1 Mini récupère des données sur Internet  (format Json) .
  4. Mes Wemos D1 Mini discutent sur Internet avec MQTT --- cette page.


Il est nécessaire de commencer par l'épisode 1, par contre les épisodes suivants peuvent être consultés dans n'importe quel ordre.


Dans la même philosophie que les expériences "Code minimal des capteurs pour Arduino" et "Code minimal des actionneurs pour Arduino", nous fournirons ici uniquement le code relatif à nos besoins de connexion, sans mettre au point une quelconque application. Donc, ici, pas besoin de connecter une led ou un capteur, donc pas de schéma de montage : vous branchez simplement votre carte en USB sur votre ordinateur, et les résultats seront visibles en mode texte dans le moniteur série de l'environnement de développement Arduino.
  • Expériences pré-requises
  • Fichiers

Étape 1 - MQTT ? qu'est-ce que c'est ?

MQTT (Message Queuing Telemetry Transport) permet l'envoi et la réception de messages de petite taille. MQTT s'appuie sur un "broker MQTT", serveur externe, qui va recevoir les données d'un système, et les redistribuer à d'autres systèmes.

MQTT est souvent utilisé pour collecter des données en provenance de petits capteurs (par exemple, capteurs de température dans un système domotique, capteurs de pollution au niveau d'une région voire d'un pays), car il a aussi comme avantage d'être peu consommateur de ressources.


MQTT est basé sur un principe d'abonnement : le système émetteur doit préciser à quel sujet ("topic") se rattache son message, et tous les systèmes qui s'étaient préalablement abonnés à ce "topic" recevront alors le message. Principe proche de Twitter ou Instagram et leurs "hashtags", donc.


On peut implémenter son propre broker MQTT (le code est libre d'usage), ou s'appuyer sur des brokers gérés par des associations ou des entreprises. Dans l'exemple ci-après, on utilise le broker des Petits Débrouillards, à utiliser avec modération.


Mais ce n'est pas l'objet du tutoriel, nous nous intéressons ici uniquement à la partie "client", c'est à dire ce qu'il faut mettre en œuvre sur nos cartes D1 mini ou ESP.

Étape 2 - Mise en œuvre sur nos petites cartes

Il existe plusieurs bibliothèques Arduino permettent de gérer des messages MQTT. Pour notre part, on utilise celle-ci (à aller chercher dans le gestionnaire de bibliothèque) :


Bibliothèque MQTT



Gestion du MQTT
Avant le Setup Importation de la bibliothèque #include <MQTT.h>
Création de l’objet MQTTClient myMQTTClient;
Dans le Setup (ou le loop) Initialisation myMQTTClient.begin(@IP Broker, Port Broker, Client Wifi) ;
Se préparer à la réception de messages myMQTTClient.onMessage(référence de la fonction à appeler sur réception d'un message) ;
Connexion au broker MQTT myMQTTClient.connect(ID unique)
Souscrire à un "topic" particulier myMQTTClient.subscribe(topic) ;
Publier un message myMQTTClient.publish(topic, message) ;
Dans le Loop Activation régulière obligatoire : myMQTTClient.loop() ;

Pour connaître toutes les autres possibilités de cette bibliothèque, voir sa référence, ici.

Code minimal :


Dans cet exemple, notre carte est à la fois émettrice (elle va envoyer des "Pings") et réceptrice sur le topic "PING_PONG". Elle va aussi répondre "Pong" sur réception d'un "Ping". En règle générale, dans les vraies applications, une carte est souvent émettrice (envoi de données d'un capteur de pollution, par exemple), et il peut y avoir une seule autre carte chargée d'exploiter les données remontées par plusieurs capteurs.
/* =========================================================================================================
 * 
 *                              CODE MINIMAL RESEAU - ETAPE 6 : Messages MQTT
 *          
 * ---------------------------------------------------------------------------------------------------------
 * Les petits Débrouillards - décembre 2022 - CC-By-Sa http://creativecommons.org/licenses/by-nc-sa/3.0/
 * ========================================================================================================= */

// Bibliothèques requises
// ATTENTION AUX MAJUSCULES & MINUSCULES ! Sinon d'autres bibliothèques, plus ou moins valides, seraient utilisées.

#include <WiFiManager.h>                          // Gestion de la connexion Wi-Fi (recherche de points d'accès)  
#include <WiFiClientSecure.h>                     // Gestion de la connexion à un serveur de données
#include <MQTT.h>                                 // Gestion des requêtes MQTT


// Variables globales

WiFiManager myWiFiManager;                        // Manager Wi-Fi
WiFiClient myWiFiClient ;                         // Client WiFi
const char* mySSID   = "AP_PetitDeb" ;            // Nom de la carte en mode Point d'Accès.
const char* mySecKey = "PSWD1234" ;               // Mot de passe associé, 8 caractères au minimum.


#define MQTT_BROKER_IP "debrouillards.ddns.net"   // Serveur sur lequel est installé le Broker MQTT.
#define MQTT_BROKER_PORT 1883                     // Port sur lequel écoute le broker MQTT

char MY_MQTT_ID[20] ;                             // Id unique de notre objet, basé sur ESP.getChipId()
const char MY_MQTT_TOPIC[] = "PING_PONG" ;        // Nom de notre topic (sujet) MQTT

MQTTClient myMQTTClient;                          // Client MQTT

bool IHaveToAnswer = false ;                      // Passe à 'true' si on doit répondre.

#define TEN_SECONDS 10000                         // On enverra un message au broker toutes les 10000 ms = 10 secondes.
unsigned long myWakeUp ;                          // Timer mis en place pour limiter le nombre d'envois au broker.

/* --------------------------------------------------------------------------------------------------------------
 * MQTT_Received : réception d'un message MQTT
 * ------------------------------------------------------------------------------------------------------------- */
void MQTT_Received(String &topic, String &payload) {

    char myTrace[80] ;

    sprintf(myTrace, "Réception sur le topic \"%s\" du message MQTT \"%s\".", topic, payload) ;
    Serial.println(myTrace) ;

    // Comme indiqué dans la documentation, il ne faut pas renvoyer de messages dans cette fonction,
    // ça risque de mal se passer (blocages, ...). Si on souhaite répondre au message reçu, il vaut
    // mieux mettre à jour une variable globale, qui sera vérifiée dans la partie loop().

    if (payload == String("Ping")) {
        IHaveToAnswer = true ;
    }
  
}

/* --------------------------------------------------------------------------------------------------------------
 *  MQTT_Connect : Connexion - ou reconnexion - au broker MQTT.
 * ------------------------------------------------------------------------------------------------------------- */
void MQTT_Connect() {

    // Vérification WiFi OK.
    
    int nbTries = 0 ;
    while (WiFi.status() != WL_CONNECTED) {
        if (nbTries++ > 10) {
            Serial.println("Connexion WiFi KO :-(") ;
            return ;
        }
        delay(500);
    }

    // Connexion au broker.
    
    Serial.println("--- Connexion MQTT, Id unique \"" + String(MY_MQTT_ID) + "\" ") ;
    nbTries = 0 ;
    while (!myMQTTClient.connect(MY_MQTT_ID)) {                                       
        Serial.print(".") ; 
        if (nbTries++ > 10) {
            Serial.println(" KO :-(") ;
            return ;
        }
        delay(500);
    }

    // Abonnement au topic.
    
    Serial.println("--- Abonnement au sujet \"" + String(MY_MQTT_TOPIC) + "\"") ;   
    myMQTTClient.subscribe(MY_MQTT_TOPIC);  
    
}
/* --------------------------------------------------------------------------------------------------------
 *  SETUP : Initialisation
 * -------------------------------------------------------------------------------------------------------- */
void setup() {

    // Initialisation de la liaison série, affichage 1er message

    Serial.begin(115200);
    delay(100) ;
    Serial.println(); 
    Serial.println("---------------------") ;
    Serial.println("Exemple messages MQTT") ;
    Serial.println("---------------------") ;

    // Tentative de connexion au Wi-Fi. Si la carte n'a pas réussi  se connecter au dernier Point d'Accès connu,
    // alors elle va se positionner en mode Point d'Accès, demandera sur l'adresse 192.168.4.1 quel nouveau
    // Point d'Accès choisir. Par défaut, on restera bloqué tant que l'utilisateur n'aura pas fait de choix.
    
    Serial.println("Connexion au Wi-Fi ...");
    if (myWiFiManager.autoConnect(mySSID, mySecKey)) {
        Serial.println(); Serial.print("Connecté ! Adresse IP : ");
        Serial.println(WiFi.localIP());
    }
    else {
        Serial.println("Connexion Wi-Fi KO :-(");     
    }

    // Initialisation du MQTT

    Serial.println("Initialisation MQTT ...");
    myMQTTClient.begin(MQTT_BROKER_IP, MQTT_BROKER_PORT, myWiFiClient);               // lancement du client MQTT ...
    myMQTTClient.onMessage(MQTT_Received);                                            // ... qui appelera la fonction MQTT_Received si un message est reçu.

    strncpy(MY_MQTT_ID, String(ESP.getChipId()).c_str(),sizeof(MY_MQTT_ID)) ;         // Fabrication de mon ID unique, sur la base du n° de puce
    MY_MQTT_ID[sizeof(MY_MQTT_ID)-1] = '\0' ;

    // Connexion au broker 

    MQTT_Connect() ;
    
    // Initialisation du timer qui sera testé dans loop() - pour faire appel au serveur seulement toutes les 10 secondes 
    // millis() est une fonction système donnant le nombre de ms depuis le lancement ou la réinitialisation de la carte.

    unsigned long myWakeUp = millis() + TEN_SECONDS ;

}

/* --------------------------------------------------------------------------------------------------------------
 *  LOOP : fonction appelée régulièrement par le système
 *  ------------------------------------------------------------------------------------------------------------- */
void loop() { 

    // Réactivation du client MQTT

    myMQTTClient.loop() ;
    delay(10) ;             // Problèmes de stabilité Wi-Fi ? (Cf. doc MQTT)

    // Reconnexion au broker MQTT si nécessaire ...

    if (!myMQTTClient.connected()) {
        MQTT_Connect();
    }

    // Si on a précédemment reçu un 'Ping', on va répondre 'Pong'

    if (IHaveToAnswer) {
        IHaveToAnswer = false ;
        Serial.println("Envoi de la réponse \"Pong\" sur le topic \"" + String(MY_MQTT_TOPIC) + "\".") ;
        myMQTTClient.publish(MY_MQTT_TOPIC, "Pong") ;
    }

    // Envoi d'un message toutes les 10 secondew.
   
    unsigned long myNow = millis() ;
    if (myNow >= myWakeUp) {
        Serial.println("Wake Up ! envoi du message \"Ping\" sur le topic \"" + String(MY_MQTT_TOPIC) + "\".") ;
        myMQTTClient.publish(MY_MQTT_TOPIC, "Ping") ;
        myWakeUp = myNow + TEN_SECONDS ;         
    }


}

Étape 3 - La suite, la suite ... à vous de jouer !

... Si vous avez suivi tous les épisodes ... :-)


Les exemples de code "minimal" fournis peuvent servir de base pour vos futures réalisations, ou pour gagner un temps précieux pendant un hackathon. Tous les codes sont regroupés dans un dossier compressé, accessible dans la rubrique "Fichiers" en début de cette page.


Pour aller plus loin, n'hésitez pas à copier ces programmes minimaux, et à les modifier en douceur pour obtenir autre chose que des traces dans le moniteur série Arduino. Juste quelques idées ...



Dernière modification 1/02/2023 par user:Philby.

Commentaires

Published